/*
 * To change this template, choose Tools | Templates and open the template in the editor.
 */
package com.fastwire.carriere.rad.snmpadapter;

import org.snmp4j.Snmp;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.smi.UdpAddress;
import org.snmp4j.mp.MessageProcessingModel;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.TransportMapping;
import org.snmp4j.smi.OctetString;
import org.snmp4j.transport.DefaultTcpTransportMapping;
import org.snmp4j.mp.MPv3;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.security.USM;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.util.MultiThreadedMessageDispatcher;
import org.snmp4j.util.ThreadPool;
import org.snmp4j.MessageDispatcherImpl;
import org.snmp4j.smi.TcpAddress;
import org.snmp4j.CommandResponder;
import org.snmp4j.CommandResponderEvent;
import org.snmp4j.PDU;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;

import com.fastwire.carriere.common.util.CommonLogger;
import com.fastwire.carriere.rad.controller.TrapMsgController;
import com.fastwire.carriere.rad.controller.TrapMsgControllerImp;
import com.fastwire.carriere.rad.entity.RadAlarm;
import com.fastwire.carriere.rad.entity.RadLog;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.io.IOException;

/**
 * @author Jun Created on 20120227
 */
public class TrapReceiver implements CommandResponder
{
    private Snmp snmp = null;

    private Address listenAddress;

    private ThreadPool threadPool;
    //private static int threadPoolSize = 2; 

    //private List<Rad> rads  = new ArrayList<Rad>();
    private Set<String> radAddresses = new HashSet<String>();

    private List<RadAlarm> radAlarms = new ArrayList<RadAlarm>();

    private List<RadLog> radLogs = new ArrayList<RadLog>();

    private static TrapReceiver trapper = null;
    
    private TrapMsgController trapCtrller = null; 
    
    // start trap
    public static void startListen(TrapMsgController ctller)
    {
        if (null == trapper)
        {
            trapper = new TrapReceiver(ctller);

            try
            {
                trapper.init();
            }
            catch (Exception ex)
            {
                CommonLogger.info(trapper, "Cannot initialize the trap.");
                ex.printStackTrace();
            }
            CommonLogger.info(trapper, "init trapper...");
        }        
    }

    private TrapReceiver(TrapMsgController ctller)
    {
        trapCtrller = ctller;
    }
    
    private static int i = 0;
    // parse the trap message;
    @Override
    public synchronized void processPdu(CommandResponderEvent event)
    {        
        System.out.println("\n................................processing PDU " + ++i +":");

        if (null == event)
        {
            System.out.println("event is null");
            CommonLogger.info(this, "event is null");
            return;
        }

        String strIP = parseAddress(event.getPeerAddress());
        if (null == strIP)
        {
            return;
        }

        System.out.println("IP = " + strIP);
        System.out.println("event = " + event.toString());
        // System.out.println("isProcessed = " + event.isProcessed()); ??

        // MessageProcessingModel msgPrc = new MPv2c();

        PDU pdu = event.getPDU();
        if (null != pdu)
        {
            // int msgType = pdu.getType(); // ??????  
            System.out.println("Trap Type: " + pdu.getType());
            // System.out.println("pdu: " + pdu.toString());
            // System.out.println("Variables = " + pdu.getVariableBindings());
            /*
            for (VariableBinding vb : pdu.getVariableBindings())
            {
                System.out.println(vb.getOid().toString() + " : " + vb.getVariable().toString());
            }
             */
            parseVariableBinding(strIP, pdu.getVariableBindings());
        }

        return;
    }

    // 
    private void parseVariableBinding(String ip, Vector<? extends VariableBinding> vecVBs)
    {
        // trap oid, // system up
        // int iTmp = vecVBs.indexOf(RadConstants.OID_trapOID);
        OID trapOID = null; // (OID) vecVBs.get(iTmp).getOid();
        for (VariableBinding vb : vecVBs)
        {
            if (vb.getOid().equals(SnmpRadConstants.OID_trapOID))
            {
                trapOID = new OID(vb.getVariable().toString());
                break;
            }
        }
        // int iTmp = vecVBs.indexOf(RadConstants.OID_trapOID);
        // OID trapOID = (OID) vecVBs.get(iTmp).getOid();

        // cold start
        if (trapOID.equals(SnmpRadConstants.OID_coldStart))
        {
            parseColdStart(ip, vecVBs);
            updateRad();
        }
        // alarm
        else if (trapOID.equals(SnmpRadConstants.OID_alarm))
        {
            parseAlarm(ip, vecVBs);
            updateRadAlarm();
        }
        /* event log
        else if (trapOID.equals(SnmpRadConstants.OID_log))
        {
            parseLog(ip, vecVBs);
            updateRadLog();
        }*/
        else
        {
            // discard
        }
    }
    
    
    private void updateRad()
    {
    	if(0 < radAddresses.size())
    	{
    		trapCtrller.updateRadByIP(radAddresses);
    		radAddresses.clear();
    	}

        /*
        if(0 < rads.size())
        {
            trapCtrller.updateRads(rads);
            rads.clear();
        }
        */
    }
    
    private void updateRadAlarm()
    {
        // 0 ....
    	if(0 < radAlarms.size())
    	{
    		trapCtrller.updateRadAlarms(radAlarms);
    		radAlarms.clear();
    	}
    }
    
    private void updateRadLog()
    {
    	if(0 < radLogs.size())
    	{
    		trapCtrller.updateRadLogs(radLogs);
    		radLogs.clear();
    	}
    }
    
    //
    private void init() throws UnknownHostException, IOException
    {
        threadPool = ThreadPool.create("Trap", 5);
        MultiThreadedMessageDispatcher dispatcher = new MultiThreadedMessageDispatcher(threadPool,
                new MessageDispatcherImpl());
        listenAddress = GenericAddress.parse(System.getProperty("snmp4j.listenAddress",
                "udp:0.0.0.0/162"));

        @SuppressWarnings("rawtypes")
        TransportMapping transport;
        if (listenAddress instanceof UdpAddress)
        {
            transport = new DefaultUdpTransportMapping((UdpAddress) listenAddress);
        }
        else
        {
            transport = new DefaultTcpTransportMapping((TcpAddress) listenAddress);
        }

        snmp = new Snmp(dispatcher, transport);
        snmp.getMessageDispatcher().addMessageProcessingModel(new MPv1());
        snmp.getMessageDispatcher().addMessageProcessingModel(new MPv2c());
        snmp.getMessageDispatcher().addMessageProcessingModel(new MPv3());

        @SuppressWarnings("static-access")
        USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(
                ((MPv3) snmp.getMessageProcessingModel(MessageProcessingModel.MPv3))
                        .createLocalEngineID()), 0);
        SecurityModels.getInstance().addSecurityModel(usm);

        snmp.listen();
        snmp.addCommandResponder(this);
    }


    /*
     * ColdStart will be processed in the parseAlarm 
     */
    private void parseColdStart(String ip, Vector<? extends VariableBinding> vecVBs)
    {
        radAddresses.add(ip);  // need this IP is enough, will do a discovery later ???

//        Rad rad = new Rad(ip);
//        //
//
//        for (VariableBinding vb : vecVBs)
//        {
//            System.out.println(vb.getOid() + " : " + vb.getVariable());
//            
//            if (vb.getOid().equals(SnmpRadConstants.OID_sysContact))
//            {
//                rad.setSysContact(vb.getVariable().toString());
//            }
//            else if (vb.getOid().equals(SnmpRadConstants.OID_sysName))
//            {
//                rad.setSysName(vb.getVariable().toString());
//            }
//            else if (vb.getOid().equals(SnmpRadConstants.OID_sysDescr))
//            {
//                rad.setSysDescr(vb.getVariable().toString());
//            }
//            else if (vb.getOid().equals(SnmpRadConstants.OID_sysObjectID))
//            {
//                rad.setSysObjectID(vb.getVariable().toString());
//            }
//            else if (vb.getOid().equals(SnmpRadConstants.OID_sysLocation))
//            {
//                rad.setSysLocation(vb.getVariable().toString());
//            }
//            else if (vb.getOid().equals(SnmpRadConstants.OID_sysServices))
//            {
//                rad.setSysServices(vb.getVariable().toString());
//            }
//        }
//
//        rads.add(rad);

    }


    /**
     * @param strIP
     * @param variableBindings
     */
    private void parseLog(String ip, Vector<? extends VariableBinding> vecVBs)
    {
        RadLog log = new RadLog(ip);
        for (VariableBinding vb : vecVBs)
        {
            System.out.println(vb.getOid() + " : " + vb.getVariable());

            // log
            if (vb.getOid().equals(SnmpRadConstants.OID_atmGenSysLogIndex))
            {
                log.setIndex(vb.getVariable().toLong());
            }
            else if (vb.getOid().equals(SnmpRadConstants.OID_atmGenSysLogMessage))
            {
                log.setMessage(vb.getVariable().toString());
            }
        }
        log.setUpdateTime(new Date());

        radLogs.add(log);
    }

    /**
     * @param strIP
     * @param variableBindings the strVB format: 1.2.3.4 = 12, 12.3.1.2 = 56,
     */
    private void parseAlarm(String ip, Vector<? extends VariableBinding> vecVBs)
    {
        RadAlarm alm = new RadAlarm(ip);

        for (VariableBinding vb : vecVBs)
        {            
            System.out.println(vb.getOid() + " : " + vb.getVariable());            
            OID oid = vb.getOid().trim();

            if(oid.equals(SnmpRadConstants.OID_sysUpTime.trim()))
            {
                alm.setSysUpTime(vb.getVariable().toString());
            }
            if (oid.equals(SnmpRadConstants.OID_sysBufferAlrCode))
            {
                alm.setAlarmCode(vb.getVariable().toString());
            }
            else if (oid.equals(SnmpRadConstants.OID_alrGenDescription))
            {
                alm.setDescription(vb.getVariable().toString()); 
            }
            else if (oid.equals(SnmpRadConstants.OID_openViewSeverity))
            {
                alm.setSeverity(vb.getVariable().toString());
            }
            else if (oid.equals(SnmpRadConstants.OID_sysBufferAlrPort))
            {
                alm.setPort(vb.getVariable().toInt());
            }
            else if (oid.equals(SnmpRadConstants.OID_sysBufferAlrCode))
            {
                alm.setAlarmCode(vb.getVariable().toString());
            }
            else if (oid.equals(SnmpRadConstants.OID_sysBufferAlrState))
            {
                alm.setState(vb.getVariable().toString());
            }
        }
        Date d = new Date();
        alm.setFirstOccurrenceDate(d);
        alm.setLastOccurrenceDate(d);
        
        radAlarms.add(alm);
    }
    
    private String parseAddress(Address addr)
    {
        String strIP;
        if (addr instanceof UdpAddress)
        {
            // strIP = addr.toString(); // "192.168.0.12/162"
            strIP = ((UdpAddress) addr).getInetAddress().getHostAddress();
        }
        else if (addr instanceof TcpAddress)
        {
            strIP = ((TcpAddress) addr).getInetAddress().getHostAddress();
        }
        else
        {
            CommonLogger.info(this, "Ip is not correct in the trap message:" + addr.toString());
            return null;
        }

        return strIP;
    }

    public static void main(String[] arg)
    {
        TrapMsgController clt = new TrapMsgControllerImp();
        TrapReceiver.startListen(clt);
        
        return;
    }
}
